#include <buola/gui/x11/auto.h>

#include <buola/algorithm/comparison.h>

#include <buola/gui/x11/cx11display.h>

#include <dlfcn.h>

#include <buola/gui/x11/cglx.h>

namespace buola { namespace gui { namespace x11 {

std::string CGLX::mGLXExtensions;
std::string CGLX::mGLExtensions;
bool CGLX::mFBConfigsInit=false;
CGLX::SFBConfig CGLX::mFBConfigs[MAX_DEPTH+1];

void CGLX::InitFBConfigs()
{
	GLXFBConfig *lFBConfigs;
	int lCount;
    lFBConfigs=glXGetFBConfigs(get_default_x11_display()->GetDisplay(),
                               get_default_x11_display()->GetScreenNumber(),&lCount);

	for(int i=0;i<=MAX_DEPTH;i++)
	{
		mFBConfigs[i].mConfig=nullptr;
		mFBConfigs[i].mMipMap=false;
		mFBConfigs[i].mYInverted=false;
		mFBConfigs[i].mTextureFormat=0;

		int lDB=SHRT_MAX;
		int lStencil=SHRT_MAX;
		int lDepth=SHRT_MAX;
		bool lMipMap=false;
		bool lRGBA=false;
		int lValue;

		for(int j=0;j<lCount;j++)
		{
			XVisualInfo *lVI;
			int lVisualDepth;

            lVI=glXGetVisualFromFBConfig(get_default_x11_display()->GetDisplay(),lFBConfigs[j]);
			if(!lVI)
			{
				continue;
			}

			lVisualDepth=lVI->depth;

			XFree(lVI);

			if(lVisualDepth!=i) continue;

			int lAlpha;
            glXGetFBConfigAttrib(get_default_x11_display()->GetDisplay(),lFBConfigs[j],
									GLX_ALPHA_SIZE,&lAlpha);
			int lBufferSize;
            glXGetFBConfigAttrib(get_default_x11_display()->GetDisplay(),lFBConfigs[j],
									GLX_BUFFER_SIZE,&lBufferSize);
			if(lBufferSize!=i&&lBufferSize-lAlpha!=i)
				continue;

			lValue=0;
			if(i==32)
			{
                glXGetFBConfigAttrib(get_default_x11_display()->GetDisplay(),lFBConfigs[j],
								GLX_BIND_TO_TEXTURE_RGBA_EXT,&lValue);
				if(lValue)
				{
					lRGBA=true;

					mFBConfigs[i].mTextureFormat=GLX_TEXTURE_FORMAT_RGBA_EXT;
				}
			}

			if(!lValue)
			{
				if(lRGBA)
					continue;

                glXGetFBConfigAttrib(get_default_x11_display()->GetDisplay(),lFBConfigs[j],
							GLX_BIND_TO_TEXTURE_RGB_EXT,&lValue);
				if(!lValue)
					continue;

				mFBConfigs[i].mTextureFormat=GLX_TEXTURE_FORMAT_RGB_EXT;
			}

			glXGetFBConfigAttrib(get_default_x11_display()->GetDisplay(),lFBConfigs[j],
							GLX_BIND_TO_MIPMAP_TEXTURE_EXT,&lValue);

			lMipMap=(lValue!=0);

            glXGetFBConfigAttrib(get_default_x11_display()->GetDisplay(),lFBConfigs[j],
							GLX_DOUBLEBUFFER,&lValue);
			if(lValue>lDB)
				continue;
			lDB=lValue;
            glXGetFBConfigAttrib(get_default_x11_display()->GetDisplay(),lFBConfigs[j],
							GLX_STENCIL_SIZE,&lValue);
			if(lValue>lStencil)
				continue;
			lStencil=lValue;
            glXGetFBConfigAttrib(get_default_x11_display()->GetDisplay(),lFBConfigs[j],
							GLX_DEPTH_SIZE,&lValue);
			if(lValue>lDepth)
				continue;
			lDepth=lValue;

            glXGetFBConfigAttrib(get_default_x11_display()->GetDisplay(),lFBConfigs[j],
						GLX_Y_INVERTED_EXT,&lValue);

			mFBConfigs[i].mYInverted=(bool)lValue;
			mFBConfigs[i].mConfig=lFBConfigs[j];
			mFBConfigs[i].mMipMap=lMipMap;
		}
	}

	if(lCount)
		XFree(lFBConfigs);

	mFBConfigsInit=true;
}

unsigned int CGLX::GetVersion()
{
	static unsigned int lVersion=0;

	if(!lVersion)
	{
		int lMin,lMaj;

        glXQueryVersion(get_default_x11_display()->GetDisplay(),&lMaj,&lMin);
		lVersion=GLX_SERVERVERSION(lMaj,lMin);
	}

	return lVersion;
}

int CGLX::GetConfig(XVisualInfo *pInfo,int pParam)
{
	int lValue;

    glXGetConfig(get_default_x11_display()->GetDisplay(),pInfo,pParam,&lValue);

	return lValue;
}

bool CGLX::HasGLXExtension(const std::string &pExt)
{
	if(mGLXExtensions.empty())
	{
        const char *lExt=glXQueryExtensionsString(get_default_x11_display()->GetDisplay(),
                                                  get_default_x11_display()->GetScreenNumber());
		mGLXExtensions=lExt;
	}

	return contains(mGLXExtensions,pExt);
}

bool CGLX::HasGLExtension(const std::string &pExt)
{
	if(mGLExtensions.empty())
	{
		mGLExtensions=(const char*)glGetString(GL_EXTENSIONS);
	}

	return contains(mGLExtensions,pExt);
}

/*namespace x11*/ } /*namespace gui*/ } /*namespace buola*/ }
